#ifndef MOCHA_DETAIL_IMAGE_HPP_INCLUDED
#define MOCHA_DETAIL_IMAGE_HPP_INCLUDED

/**
 * @file
 */

#include <algorithm>
#include <cstdint>
#include <iostream>
#include <string>
#include <vector>
#include <boost/filesystem.hpp>
#include <boost/noncopyable.hpp>
#include <boost/iostreams/device/array.hpp>
#include <boost/iostreams/device/file.hpp>
#include <boost/iostreams/stream.hpp>
#include "mocha/color.hpp"
#include "mocha/enum_string.hpp"
#include "mocha/image_plugin.hpp"
#include "mocha/object_importer.hpp"
#include "mocha/plugin_proxy.hpp"
#include "mocha/pointers.hpp"
#include "mocha/region.hpp"

namespace mocha { namespace graphics {
/**
 * 
 */
class image : public plugin_proxy<image_plugin>, public boost::noncopyable {
public:
	image(plugin_factory_ptr_t factory, const std::string& name, const object_importer& importer);
	virtual ~image();
	/**
	 * Loads an image file from the file system.
	 * @param file File to load.
	 * @return Zero or greater on success, or less than zero on failure.
	 */
	int load(const boost::filesystem::path& file);
	/**
	 * Loads an image from an input stream. The data is expected to be in the original file format (e.g., a file from disk or buffered in memory, not raw pixels).
	 * @param in Stream.
	 * @param size Estimated size of the image data in the stream. Defaults to 1MiB.
	 * @return Zero or greater on success, or less than zero on failure.
	 */
	int load(std::istream& in, size_t size = 1000000u);
	/**
	 * Loads an image from an input stream. The data is expected to be raw pixels, not the original file format (if any).
	 * @param in Stream.
	 * @param width
	 * @param height
	 * @param fmt Format of the pixel data.
	 * @return Zero or greater on success, or less than zero on failure.
	 */
	int load(std::istream& in, size_t width, size_t height, const pixel_format& fmt);
	/**
	 * Loads an image from a stream. The data is expected to be in the original file format (e.g., a file from disk or buffered in memory, not raw pixels).
	 * @param T Device model of the stream.
	 * @param in Stream.
	 * @param size Estimated size of the image data in the stream. Defaults to 1MiB.
	 * @return Zero or greater on success, or less than zero on failure.
	 */
	template<typename T>
	int load(boost::iostreams::stream<T>& in, size_t size = 1000000u);
	/**
	 * Loads an image from an input stream. The data is expected to be raw pixels, not the original file format (if any).
	 * @param T Device model of the stream.
	 * @param in Stream.
	 * @param width
	 * @param height
	 * @param fmt Format of the pixel data.
	 * @return Zero or greater on success, or less than zero on failure.
	 */
	template<typename T>
	int load(boost::iostreams::stream<T>& in, size_t width, size_t height, const pixel_format& fmt);
	/**
	 * Frees any loaded data.
	 */
	void free();
	/**
	 * Creates a new texture and loads it with the image's data.
	 * @return A texture loaded with the image data.
	 */
	//texture_ptr_t to_texture(graphics_ptr_t gfx) const;
	/**
	 * @return Copy of the image, or null on failure.
	 */
	image_ptr_t clone() const;
	/**
	 * Crops the image.
	 * @param region
	 * @return Zero or greater on success, or less than zero on failure.
	 */
	int crop(const region<int>& region);
	/**
	 * Scales (resizes) the image.
	 * @param width Width (after scaling).
	 * @param height Height (after scaling).
	 * @return Zero or greater on success, or less than zero on failure.
	 */
	int scale(size_t width, size_t height);
	/**
	 * Attempts to make the image data more compliant by altering pixel format and dimensions.
	 * Can reduce graphical glitches (e.g., artifacts) and improve performance, especially on older hardware.
	 * Dimensions are scaled to the nearest (greater) power of two.
	 * Pixels are converted to unsigned byte RGB(A) format.
	 */
	void conform();
	/**
	 * Attempts to change the format of the pixel data in memory.
	 * @param fmt 
	 * @return Zero or greater on success, or less than zero on failure.
	 */
	int convert(const pixel_format& fmt);
	/**
	 * Gets loaded pixel data (read/write).
	 * @return Stream device attached to the data.
	 */
	boost::iostreams::array pixels();
	/**
	 * Gets loaded pixel data (read only).
	 * @return Stream device attached to the data.
	 */
	boost::iostreams::array_source pixels() const;
	/**
	 * Exposes the raw data buffer.
	 * The size of the buffer can alternatively be determined using the pixel format and dimensions.
	 * @param data Receives the starting address of the raw data buffer. Set to null if no data is loaded.
	 * @param size Optional parameter to receive the size of the buffer.
	 * @see pixels
	 */
	void raw(std::int8_t* data, size_t* size = NULL);
	/**
	 * @param fmt 
	 * @return 
	 */
	int format(pixel_format& fmt) const;
	/**
	 * @return
	 */
	size_t width() const;
	/**
	 * @return
	 */
	size_t height() const;
	/**
	 * @return True if data is loaded, otherwise false.
	 */
	bool loaded() const;
};
/**
 * Finds the required buffer size for an image.
 * @param width
 * @param height
 * @param format
 * @return Size of the buffer needed to hold such image data.
 */
size_t image_buffer_size(size_t width, size_t height, const pixel_format& format);
}}

#endif

